#include "RenderTexture.h"

#include "Settings.h"

RenderTexture::RenderTexture()
{
	numOfTargets = 0;
	targetWidth = 0;
	targetHeight = 0;
	depthState = None;
}

RenderTexture::~RenderTexture()
{
	delete[] targets;
}
 
bool RenderTexture::Init(int targetCount, int width, int height, DepthState depthState, 
							GLenum* textureTypes,
							GLenum* formats, 
							GLenum* types)
{
	numOfTargets = targetCount;
	targetWidth = width;
	targetHeight = height;
	this->depthState = depthState;
	this->textureTypes = textureTypes;

	this->formats = formats;
	this->types = types;

	InitRenderTextures();

	if(depthState == DepthTexture)
	{
		InitDepthTexture();
	}
	else if(depthState == DepthBuffer)
	{
		InitDepthBuffer();
	}
	
	InitFBO();

	if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
		return false;

	return true;
}

void RenderTexture::InitFBO()
{
	glGenFramebuffers(1, &fbo);
	glBindFramebuffer(GL_FRAMEBUFFER, fbo);

	for(int i=0; i<numOfTargets; i++)
	{
		glFramebufferTexture(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, targets[i], 0);
	}

	if(depthState == DepthTexture)
		glFramebufferTexture(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTex, 0);
	else if(depthState == DepthBuffer)
		glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthTex);
}

void RenderTexture::InitRenderTextures()
{
	targets = new GLuint[numOfTargets];
	glGenTextures(numOfTargets, targets);

	GLenum format = GL_RGBA;						//if unspecified, we use RGBA
	GLenum type = GL_UNSIGNED_BYTE;					//if unspecified, we use UNSIGNED_BYTE
	GLenum textureType = GL_TEXTURE_2D;

	for(size_t i = 0; i < numOfTargets; i++)
	{
		GLuint targetID = targets[i];

		if(formats != nullptr)
			format = formats[i];

		if(types != nullptr)
			type = types[i];

		if(textureTypes != nullptr)
			textureType = textureTypes[i];
		
		glBindTexture(textureType, targetID);
		glTexImage2D(textureType, 0, (GLint) format, targetWidth, targetHeight, 0, format, type, 0);
		glTexParameteri(textureType, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
		glTexParameteri(textureType, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		glBindTexture(textureType, 0);
	}
}

void RenderTexture::InitDepthTexture()
{
	glGenTextures(1, &depthTex);
	glBindTexture(GL_TEXTURE_2D, depthTex);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, targetWidth, targetHeight, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0); 
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}
	
void RenderTexture::InitDepthBuffer()
{
	glGenRenderbuffers(1, &depthTex);
	glBindRenderbuffer(GL_RENDERBUFFER, depthTex);
	glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, targetWidth, targetHeight);

}

GLuint RenderTexture::GetTextureID(int targetIndex)
{
	return targets[targetIndex];
}

void RenderTexture::Bind()
{
	glBindFramebuffer(GL_FRAMEBUFFER, fbo);
	
	glClearColor(0, 0, 0, 0);
	glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
	glViewport(0,0,targetWidth, targetHeight); 
}

void RenderTexture::Unbind()
{
	glBindFramebuffer(GL_FRAMEBUFFER, 0);
	glViewport(0,0, WINDOW_WIDTH, WINDOW_HEIGHT); 
}